package com.tyqx.hashchainutil.com.example.demo.service.impl;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/*import org.app.chaincode.invocation.InvokeChaincode;
import org.app.client.CAClient;
import org.app.client.ChannelClient;
import org.app.client.FabricClient;
import org.app.config.Config;
import org.app.user.UserContext;
import org.app.util.Util;*/
import com.tyqx.hashchainbean.module.HashIndex;
import com.tyqx.hashchainutil.com.example.demo.service.FabricService;
import com.tyqx.hashchainutil.common.CreateWallet;
import com.tyqx.hashchainutil.org.app.chaincode.invocation.InvokeChaincode;
import com.tyqx.hashchainutil.org.app.client.CAClient;
import com.tyqx.hashchainutil.org.app.client.ChannelClient;
import com.tyqx.hashchainutil.org.app.client.FabricClient;
import com.tyqx.hashchainutil.org.app.config.Config;
import com.tyqx.hashchainutil.org.app.user.UserContext;
import com.tyqx.hashchainutil.org.app.util.Util;
import org.hyperledger.fabric.sdk.*;
import org.json.JSONObject;
import org.springframework.stereotype.Service;


@Service("fabricService")
public class FabricServiceImpl implements FabricService {
    private static final byte[] EXPECTED_EVENT_DATA = "!".getBytes(UTF_8);
    private static final String EXPECTED_EVENT_NAME = "event";


    public String query(String param) {
        try {
            Util.cleanUp();
            String caUrl = Config.CA_ORG1_URL;
            CAClient caClient = new CAClient(caUrl, null);
            // Enroll Admin to Org1MSP
            UserContext adminUserContext = new UserContext();
            adminUserContext.setName(Config.ADMIN);
            adminUserContext.setAffiliation(Config.ORG1);
            adminUserContext.setMspId(Config.ORG1_MSP);
            caClient.setAdminUserContext(adminUserContext);
            adminUserContext = caClient.enrollAdminUser(Config.ADMIN, Config.ADMIN_PASSWORD);

            FabricClient fabClient = new FabricClient(adminUserContext);

            ChannelClient channelClient = fabClient.createChannelClient(Config.CHANNEL_NAME);
            Channel channel = channelClient.getChannel();
            Peer peer = fabClient.getInstance().newPeer(Config.ORG1_PEER_0, Config.ORG1_PEER_0_URL);
            EventHub eventHub = fabClient.getInstance().newEventHub("eventhub01", "grpc://" + Config.baseUrl + ":7053");
            Orderer orderer = fabClient.getInstance().newOrderer(Config.ORDERER_NAME, Config.ORDERER_URL);
            channel.addPeer(peer);
            channel.addEventHub(eventHub);
            channel.addOrderer(orderer);
            channel.initialize();

            Logger.getLogger(FabricServiceImpl.class.getName()).log(Level.INFO, "Querying for all cars ...");
            Collection<ProposalResponse> responsesQuery;
            if (param == null || "".equals(param)) {
                responsesQuery = channelClient.queryByChainCode("mycc", "queryrange", null);
            } else {
                responsesQuery = channelClient.queryByChainCode("mycc", "query", new String[]{param});

            }
            Map m = new HashMap<String, String>();
            int i = 0;
            for (ProposalResponse pres : responsesQuery) {
                String stringResponse = new String(pres.getChaincodeActionResponsePayload());
                m.put("" + i, stringResponse);
                Logger.getLogger(FabricServiceImpl.class.getName()).log(Level.INFO, stringResponse);
            }
            channel.shutdown(false);
            return m.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    public HashIndex insert(UserContext user, JSONObject hashObj) throws Exception {

        CreateWallet createWallet = new CreateWallet();
        UserContext adminUserTLS = createWallet.enrollAdminUserTLS();
        //createWallet.enrollAdminUserTLS();
        FabricClient fabClient = new FabricClient(adminUserTLS);

        UserContext userTLS = Util.readUserContextTLS(user.getAffiliation(), user.getName());

        ChannelClient channelClient = fabClient.createChannelClient(Config.CHANNEL_NAME);
        Channel channel = channelClient.getChannel();

        Properties peerProperties = Util.gete2ePro("peer", Config.ORG1_PEER_0);

        Enrollment enrollment = userTLS.getEnrollment();
        String clientCertString = enrollment.getCert().toString();

        String clientKeyString = "-----BEGIN PRIVATE KEY-----\n";
        clientKeyString += Base64.getEncoder().encodeToString(enrollment.getKey().getEncoded())+"\n";
        clientKeyString += "-----END PRIVATE KEY-----\n";

        peerProperties.put("clientCertBytes", clientCertString.getBytes());
        peerProperties.put("clientKeyBytes", clientKeyString.getBytes());

        Peer peer = fabClient.getInstance().newPeer(Config.ORG1_PEER_0, Config.ORG1_PEER_0_URL, peerProperties);
        EventHub eventHub = fabClient.getInstance().newEventHub("eventhub01", "grpcs://" + Config.peer0Org1BaseUrl + ":7053", peerProperties);

        Properties ordererProperties = Util.gete2ePro("orderer", "orderer0.hashchain.com");
        Orderer orderer = fabClient.getInstance().newOrderer(Config.ORDERER0_NAME, Config.ORDERER0_URL, ordererProperties);
        channel.addPeer(peer);
        channel.addEventHub(eventHub);
        channel.addOrderer(orderer);
        channel.initialize();

        TransactionProposalRequest request = fabClient.getInstance().newTransactionProposalRequest();

        ChaincodeID ccid = ChaincodeID.newBuilder().setName(Config.HASHCHAIN_CHAINCODE_NAME).build();

        request.setChaincodeID(ccid);
        request.setFcn("insert");

//            String[] arguments1 = { "1", "2", "3" };
        String [] arguments = { hashObj.toString() };
        request.setArgs(arguments);
        request.setProposalWaitTime(5000);

        Map<String, byte[]> tm2 = new HashMap<>();
        tm2.put("HyperLedgerFabric", "TransactionProposalRequest:JavaSDK".getBytes(UTF_8));
        tm2.put("method", "TransactionProposalRequest".getBytes(UTF_8));
        tm2.put("result", ":)".getBytes(UTF_8));
        tm2.put(EXPECTED_EVENT_NAME, EXPECTED_EVENT_DATA);
        request.setTransientMap(tm2);

        /*Collection<ProposalResponse> responses = channelClient.sendTransactionProposal(request);
        for (ProposalResponse res : responses) {
            ChaincodeResponse.Status status = res.getStatus();
            Logger.getLogger(InvokeChaincode.class.getName()).log(Level.INFO, "Invoked createCar on " + Config.CHAINCODE_1_NAME + ". Status - " + status);
        }
        return null;*/

        BlockEvent.TransactionEvent event = channelClient.sendTransaction(request);
        long blockNum=event.getBlockEvent().getBlockNumber();
        String transactionID=event.getTransactionID();
        Date date=event.getTimestamp();
        channel.shutdown(false);
        HashIndex hashIndex=new HashIndex();
        hashIndex.setBlockNum(blockNum);
        hashIndex.setTxTimeStamp(date);
        hashIndex.setTxId(transactionID);
        hashIndex.setHashValue(hashObj.get("hashValue").toString());
        return hashIndex;

    }



    public String invoke(String a, String b, String num) {
        try {
            Util.cleanUp();
            String caUrl = Config.CA_ORG1_URL;
            CAClient caClient = new CAClient(caUrl, null);
            // Enroll Admin to Org1MSP
            UserContext adminUserContext = new UserContext();
            adminUserContext.setName(Config.ADMIN);
            adminUserContext.setAffiliation(Config.ORG1);
            adminUserContext.setMspId(Config.ORG1_MSP);
            caClient.setAdminUserContext(adminUserContext);
            adminUserContext = caClient.enrollAdminUser(Config.ADMIN, Config.ADMIN_PASSWORD);

            FabricClient fabClient = new FabricClient(adminUserContext);

            ChannelClient channelClient = fabClient.createChannelClient(Config.CHANNEL_NAME);
            Channel channel = channelClient.getChannel();
            Peer peer = fabClient.getInstance().newPeer(Config.ORG1_PEER_0, Config.ORG1_PEER_0_URL);
            Peer peer2 = fabClient.getInstance().newPeer(Config.ORG2_PEER_0, Config.ORG2_PEER_0_URL);
            EventHub eventHub = fabClient.getInstance().newEventHub("eventhub01", "grpcs://" + Config.ORG1_PEER_0 + ":7053");
            EventHub eventHub2 = fabClient.getInstance().newEventHub("eventhub02", "grpcs://" + Config.ORG1_PEER_0 + ":9053");
            Orderer orderer = fabClient.getInstance().newOrderer(Config.ORDERER_NAME, Config.ORDERER_URL);
            channel.addPeer(peer);
            channel.addPeer(peer2);
            channel.addEventHub(eventHub);
            channel.addOrderer(orderer);
            channel.initialize();

            TransactionProposalRequest request = fabClient.getInstance().newTransactionProposalRequest();
            ChaincodeID ccid = ChaincodeID.newBuilder().setName(Config.CHAINCODE_1_NAME).build();
            request.setChaincodeID(ccid);
            request.setFcn("invoke");
            String[] arguments = {a, b, num};
            request.setArgs(arguments);
            request.setProposalWaitTime(1000);

            Map<String, byte[]> tm2 = new HashMap<>();
            tm2.put("HyperLedgerFabric", "TransactionProposalRequest:JavaSDK".getBytes(UTF_8));
            tm2.put("method", "TransactionProposalRequest".getBytes(UTF_8));
            tm2.put("result", ":)".getBytes(UTF_8));
            tm2.put(EXPECTED_EVENT_NAME, EXPECTED_EVENT_DATA);
            request.setTransientMap(tm2);
            Collection<ProposalResponse> responses = channelClient.sendTransactionProposal(request);
            for (ProposalResponse res : responses) {
                ChaincodeResponse.Status status = res.getStatus();
                Logger.getLogger(InvokeChaincode.class.getName()).log(Level.INFO, "Invoked createCar on " + Config.CHAINCODE_1_NAME + ". Status - " + status);
            }
            channel.shutdown(false);
            return "{code:success}";
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}
